package com.duanjw.java.proxy.custom.util;

import com.duanjw.java.proxy.custom.CustomInvocationHandler;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

/**
 * @author duanjw
 */
public class ProxyUtil {
    /**
     * 1. .java
     * 2. .class
     * 3. new
     *
     * @param targetInterface 目标对象的接口
     * @param h
     * @return
     */
    public static Object newInstance(Class targetInterface, CustomInvocationHandler h) {
        Object proxy = null;
        String interfaceName = targetInterface.getSimpleName();
        Method[] methods = targetInterface.getMethods();
        String line = "\n";
        String tab = "\t";
        String content = "";
        String packageContent = "package com.duanjw.java.proxy;" + line;
        String importContent = "import " + targetInterface.getName() + ";" + line + "import java.lang.reflect.Method;" + line + "import com.duanjw.java.proxy.custom.CustomInvocationHandler;" + line;
        String classContent = "public class $Proxy implements " + interfaceName + " { " + line;
        String filedContent = tab + "private CustomInvocationHandler h;" + line;
        String constructorContent = tab + "public $Proxy(CustomInvocationHandler h) {" + line +
                tab + tab + "this.h = h;" +
                line + tab + "}" + line;

        String methodContent = "";
        for (Method method : methods) {
            String returnContent = "";
            String returnTypeName = method.getReturnType().getSimpleName();
            if (!"void".equals(returnTypeName)) {
                returnContent = "return ("+returnTypeName+")";
            }
            String methodName = method.getName();

            // 方法的参数
            String methodParametersContent = "";
            String params = "";
            Class<?>[] parameterTypes = method.getParameterTypes();
            String classNames = "";
            for (int i = 0; i < parameterTypes.length; i++) {
                String simpleName = parameterTypes[i].getSimpleName();
                methodParametersContent += simpleName + " i" + i + ", ";
                params += " i" + i + ", ";
                classNames += parameterTypes[i].getName() + ".class" + ", ";
            }
            if (methodParametersContent.endsWith(", ")) {
                methodParametersContent = methodParametersContent.substring(0, methodParametersContent.length() - 2);
                params = ",new Object[]{" + (params.substring(0, params.length() - 2) + "}");
                classNames = "," + classNames.substring(0, classNames.length() - 2);
            } else {
                params = ", null";
            }
            methodContent += tab + "@Override " + line
                    + tab + "public " + returnTypeName + " " + methodName + " (" + methodParametersContent + ") {" + line
                    + tab + tab + "try {" + line
                    + tab + tab + tab + "Method method = Class.forName(\"" + targetInterface.getName() + "\").getDeclaredMethod(\"" + methodName + "\"" + classNames + ");" + line
                    + tab + tab + tab + returnContent + "h.invoke(method" + params + ");" + line

                    + tab + tab + "} catch (Throwable e){" + line +
                    tab + tab + tab + "e.printStackTrace();" + line +
                    tab + tab + "}" + line
                    + ("".equals(returnContent)? "" : "return null;")
                    + tab + "}" + line;
        }

        content = packageContent + importContent + classContent + filedContent + constructorContent + methodContent + line + "}";

        File file = null;
        try {
            // java文件输出路径，如果类的包名（package）不是空，则文件路径必须包含包名，否则会有找不到类的异常
            file = new File("/Users/duanjw/Documents/code/my/com/duanjw/java/proxy/$Proxy.java");
            if (!file.exists()) {
                if (!file.getParentFile().exists()) {
                    file.getParentFile().mkdirs();
                }
                file.createNewFile();
            }
            FileWriter fileWriter = new FileWriter(file);
            fileWriter.write(content);
            fileWriter.close();


            // 编译java到class
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
            Iterable iterable = fileManager.getJavaFileObjects(file);

            JavaCompiler.CompilationTask compilationTask = compiler.getTask(null, fileManager, null, null, null, iterable);
            compilationTask.call();
            fileManager.close();

            URL[] urls = new URL[]{new URL("file:/Users/duanjw/Documents/code/my/")};
            URLClassLoader urlClassLoader = new URLClassLoader(urls);
            Class<?> clazz = urlClassLoader.loadClass("com.duanjw.java.proxy.$Proxy");
            Constructor<?> constructor = clazz.getConstructor(CustomInvocationHandler.class);
            proxy = constructor.newInstance(h);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return proxy;
    }
}
